package org.shadow.core.key;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.shadow.core.key.exception.InvalidKeyPatternException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author zhouw
 * @version 0.1
 * @since 2016.12.02
 */
public final class SimpleKeyFormatter implements KeyFormatter {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SimpleKeyFormatter.class);
	
	private String pattern;
	
	private List<Formatter> formatters;
	
	private boolean inited;
	
	public SimpleKeyFormatter() {}
	
	public SimpleKeyFormatter(String pattern) throws InvalidKeyPatternException {
		this.pattern = pattern;
		init();
	}
	
	private void init() throws InvalidKeyPatternException {
		if (isInited()) {
			return;
		}
		formatters = new ArrayList<Formatter>();
		if (this.pattern == null || this.pattern.trim().length() == 0) {
			throw new InvalidKeyPatternException("the format of key is null");
		}
		
		StringBuilder keyBuilder = new StringBuilder();
		int flag = 0;
		for (int i = 0; i < pattern.length(); i++) {
			char c = pattern.charAt(i);
			
			if (c == '{') {
				if (flag != 0) {
					throw new InvalidKeyPatternException("the pattern of key is invalid");
				}
				flag = 1;
				if (keyBuilder.length() > 0) {
					Formatter formatter = new SimpleTextFormatter(keyBuilder.toString());
					formatters.add(formatter);
					keyBuilder.setLength(0);
				}
				
			} else if (c == '}') {
				if (flag != 1 || keyBuilder.length() == 0) {
					throw new InvalidKeyPatternException("the pattern of key is invalid");
				}
				Formatter formatter = null;
				if (keyBuilder.charAt(0) == '#') {
					// sequence
					formatter = new SequenceFormatter(keyBuilder.toString());
				} else {
					// date
					formatter = new DateFormatter(keyBuilder.toString());
				}
				formatters.add(formatter);
				keyBuilder.setLength(0);
				flag = 0;
			} else {
				keyBuilder.append(c);
			}
		}
	}

	@Override
	public String format(int sequence) throws InvalidKeyPatternException  {
		if (!isInited()) {
			init();
		}
		StringBuilder sb = new StringBuilder();
		for (Formatter formatter : formatters) {
			sb.append(formatter.format(sequence));
		}
		return sb.toString();
	}

	public String getPattern() {
		return pattern;
	}

	@Override
	public void setPattern(String pattern) throws InvalidKeyPatternException {
		this.pattern = pattern;
		init();
	}
	
	private interface Formatter {
		public String format(int sequence) throws InvalidKeyPatternException;
	};
	
	private static class SimpleTextFormatter implements Formatter {
		
		private String text;
		
		public SimpleTextFormatter(String text) {
			this.text = text;
		}

		@Override
		public String format(int sequence) {
			if (text != null && text.length() > 0)
				return text;
			
			return "";
		}
	}
	
	private static class DateFormatter implements Formatter {
		
		private String pattern;
		
		public DateFormatter(String pattern) {
			this.pattern = pattern;
		}	

		@Override
		public String format(int sequence) throws InvalidKeyPatternException {
			try {
				SimpleDateFormat sdf = new SimpleDateFormat(pattern);
				return sdf.format(Calendar.getInstance().getTime());
			} catch (Exception e) {
				LOGGER.error("invalid date pattern", e);
				throw new InvalidKeyPatternException("invalid date pattern", e);
			}
		}
	}
	
	private static class SequenceFormatter implements Formatter {
		
		private String pattern;
		
		public SequenceFormatter(String pattern) {
			this.pattern = pattern;
		}

		@Override
		public String format(int sequence) {
			int length = pattern.length();
			int powValue = Double.valueOf(Math.pow(10, length)).intValue();
			if ((powValue - 1) < sequence) {
				LOGGER.error("sequence is bigger than key pattern's max value");
				throw new InvalidKeyPatternException("sequence is bigger than key pattern's max value");
			}
			return String.valueOf(powValue + sequence).substring(1);
		}
	}

	public boolean isInited() {
		return inited;
	}

	public void setInited(boolean inited) {
		this.inited = inited;
	}
	
}

